#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = 'z417'

import yaml
import jinja2
import functools
import subprocess
import os
from webrunner.common import BASEDIR, L, to_Dict, sys_conf_path
from webrunner.common.exceptions import EnvNotFound, FileFormatError
from configparser import RawConfigParser
from selenium import webdriver
from selenium.common.exceptions import WebDriverException, SessionNotCreatedException
import pytest
import psutil
from webrunner.common.myDriver import MyDriver
import re
import sys
import platform
# from win32api import GetSystemMetrics


def singleton(cls):
    """
    Singleton decorator
    :param cls: the object you want to set singleton
    :return:
    """
    instances = {}

    @functools.wraps(cls)
    def _wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return _wrapper


def set_env(**kw):
    for k, v in kw.items():
        os.environ[k] = v


def get_env(variable):
    try:
        return os.environ[variable]
    except KeyError:
        raise EnvNotFound(variable)


def load_dot_env(dot_env):
    dot_env = os.path.join(BASEDIR, dot_env)
    if not os.path.isfile(dot_env):
        msg = 'Can not find --dot-env file "{}"'.format(dot_env)
        return 1, msg
    msg = "Loading environment variables from {}".format(dot_env)
    env_var = {}
    with open(dot_env, 'r', encoding='utf-8') as f:
        for line in f:
            if "=" in line:
                var, value = line.split("=", 1)
            elif ":" in line:
                var, value = line.split(":", 1)
            else:
                raise FileFormatError(".env format error")
            env_var[var.strip()] = value.strip()
    return env_var, msg


def shell(cmd):
    """
    Execute commands in terminal
    :param cmd: support windows and Linux
    :return:
    """
    output, errors = subprocess.Popen(
        cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
    o = output if output else errors
    try:
        return o.decode('utf-8')
    except UnicodeError:
        return o.decode('gb2312')


def get_ini(file):
    __ini__ = RawConfigParser()
    __ini__.read(file, encoding='utf-8')
    return __ini__


def end_with(*endstring):
    ends = endstring

    def run(s):
        f = map(s.endswith, ends)
        if True in f:
            return s
    return run


def create_driver(driverPath, options):
    for i in range(3):
        try:
            __driver = webdriver.Chrome(
                executable_path=driverPath, options=options)
            break
        except SessionNotCreatedException as e:
            L.error(e)
            pytest.exit('Version issue', -1)
        except WebDriverException as e:
            L.warning(e)
            proc_name = os.path.basename(driverPath).split('.')[0]
            for proc in psutil.process_iter():
                if proc.name() == proc_name:
                    proc.kill()
    return __driver

def chrome_driver():
    if sys.platform.startswith('linux'):
        chromedriver = "chromedriver"
    elif sys.platform.startswith('win'):
        chromedriver = "chromedriver.exe"
    elif sys.platform.startswith('darwin'):
        chromedriver = "chromedriver"
    else:
        print('无法识别当前系统')
    return chromedriver

@singleton
def get_driver(driverType=chrome_driver()):
    __driverPath__ = os.path.join(BASEDIR, 'driver', driverType)
    if os.path.exists(__driverPath__):
        L.info("use {}".format(__driverPath__))
    else:
        pytest.exit(
            "Can't find '{}' in driver directory".format(driverType), -1)

    mode = get_env('mode')
    if mode.startswith('on'):
        weight = 1920
        height = 1080
        r = re.compile(r'on:[1-9]\d{2,3}[\*]{,1}[1-9]\d{2,3}$')
        if mode == 'on':
            L.info("use default HVGA settings: {}*{}".format(weight, height))
        elif r.match(mode):
            w_h = mode.split(':')[1]
            weight, height = [int(i) for i in w_h.split('*')]
            L.info('set HVGA: {}*{}'.format(weight, height))
        else:
            L.warning(
                'inviled HVGA settings, use default: {}*{}'.format(weight, height))
        from selenium.webdriver.chrome.options import Options
        options = Options()
        options.add_argument('--no-sandbox')
        options.add_argument('--headless')
        options.add_argument('--disable-dev-shm-usage')
        options.add_argument('--disable-gpu')
        options.add_argument('--disable-features=NetworkService')
        options.add_argument("--disable-features=VizDisplayCompositor")
        options.add_argument("user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'")
        # info=0,warn=1,error=2,fatal=4
        options.add_argument('log-level=3')
        options.add_argument('--window-size=%d, %d' % (weight, height))
    else:
        options = None

    __driver = create_driver(__driverPath__, options)
    return MyDriver(__driver)


def load_yml(yml):
    with open(yml, 'r', encoding='utf-8') as f:
        content = yaml.safe_load_all(f)
        for c in content:
            yield c


def parse_yml(ymlDir, needList=False):
    L.info('parse yml Path:' + ymlDir)
    if needList:
        contents = []
    else:
        contents = {}
    if os.path.isdir(ymlDir):
        for root, dirs, files in os.walk(ymlDir):
            for name in filter(end_with('yml'), files):
                absolutePathOfYml = os.path.join(root, name)
                for c in load_yml(absolutePathOfYml):
                    c = to_Dict(c)
                    contents.append(c) if needList else contents.update(c)
    else:
        for c in load_yml(ymlDir):
            c = to_Dict(c)
            contents.append(c) if needList else contents.update(c)
    return contents


def get_template(template):
    template_loader = jinja2.FileSystemLoader(
        searchpath=os.path.join(sys_conf_path, 'template'))
    template_env = jinja2.Environment(loader=template_loader)
    return template_env.get_template(template)


def gen_page_list(ymlDir):
    """
    make ***.yml to dict like below
    :return: {
                "Baidu":[
                            {
                                "search":{
                                        "type":"class",
                                        "value":"s_ipt",
                                        "time_out":30
                                }
                            }
                ]
    }
    """
    _page_list = {}
    pages = parse_yml(ymlDir)
    for page, value in pages.items():
        locators = value['locators']  # list
        locator_names = []
        for locator in locators:
            __locator_info__ = {}
            name = locator.pop('name')
            __locator_info__[name] = locator
            locator_names.append(__locator_info__)
        _page_list[page] = locator_names
    return _page_list


def gen_page_py(ymlDir, pagesP):
    """
    use the template engine(Jinja2) to generated pages.py
    """
    page_list = gen_page_list(ymlDir)
    L.debug(page_list)
    _templateVars = {
        'page_list': page_list
    }
    template = get_template('pages.template')
    pagesPath = os.path.join(BASEDIR, pagesP)
    with open(pagesPath, 'w', encoding='utf-8') as f:
        f.write(template.render(_templateVars))


def DDT(yml=None) -> list:
    caseList = parse_yml(os.path.join(BASEDIR, yml), True)
    tagstr = get_env('tag')
    tag = tagstr.split(',') if tagstr else []
    if tag:
        filterCase = []
        tag = [x.strip() for x in tag]
        for case in caseList:
            ctag = case.get('tag', None)
            if ctag:
                if list(set(ctag).intersection(set(tag))):
                    filterCase.append(case)
                else:
                    continue
        return filterCase
    else:
        return caseList


def is_var_or_func(content):
    dolloarRegexCompile = re.compile(r"\$\$")
    variableRegexCompile = re.compile(r"\$\{(\w+)\}|\$(\w+)")
    functionRegexCompile = re.compile(r"\$\{(\w+)\(([\$\w\.\-/\s=,]*)\)\}")
    if not isinstance(content, (str, bytes)):
        return False
    try:
        matchStartPosition = content.index('$', 0)
    except ValueError:
        return False
    while matchStartPosition < len(content):
        dollarMatch = dolloarRegexCompile.match(content, matchStartPosition)
        if dollarMatch:
            matchStartPosition = dollarMatch.end()
            continue
        funcMatch = functionRegexCompile.match(content, matchStartPosition)
        if funcMatch:
            return True

        varMatch = variableRegexCompile.match(content, matchStartPosition)
        if varMatch:
            return True
        return False

# TODO: sql


if __name__ == '__main__':
    # v, d = load_dot_env()
    # L.info(v)
    # L.info(d)
    # set_env(**v)
    # L.info(get_env('BASE_URL'))
    # L.info(get_ini('../cfg/logConf.ini').get('handler_streamHandler', 'level'))
    os.environ['tag'] = 'if,you,want,rainbow,have to,deal,rain'
    os.environ['tag'] = 'smoke'
    for d in DDT('../DDT'):
        L.info(d)
